package com.aps.server.service;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import com.aps.algorithm.entity.BasicEquipSchedulingResult;
import com.aps.algorithm.mapper.BasicEquipSchedulingResultMapper;
import com.aps.algorithm.parameter.GanttChartParameter;
import com.aps.common.code.DistributedSnowflakeIdGenerator;
import com.aps.common.constant.Constants;
import com.aps.common.entity.ApsDayOfWeek;
import com.aps.common.entity.ApsInterval;
import com.aps.common.exception.SchedulingInvalidateException;
import com.aps.common.model.ApsParameterModel;
import com.aps.common.model.SchedulingResult;
import com.aps.common.model.genetic.Order;
import com.aps.common.model.standardAps.StandardApsEquipment;
import com.aps.common.model.standardAps.StandardApsOrder;
import com.aps.common.model.standardAps.StandardApsOrderProcess;
import com.aps.common.redis.RedisUtils;
import com.aps.common.service.TestGenericService;
import com.aps.framework.exception.BusinessException;
import com.aps.server.context.ValidateContext;
import com.aps.server.dto.Device;
import com.aps.server.dto.GanttChartDto;
import com.aps.server.dto.SchedulingPlanGenerateDto;
import com.aps.server.dto.Task;
import com.aps.server.entity.*;
import com.aps.server.mapper.ApsAlgorithmHeuristicConfigMapper;
import com.aps.server.mapper.ApsOrderMapper;
import com.aps.server.mapper.BasicEquipMapper;
import com.aps.server.utils.converter.EquipmentConverter;
import com.aps.server.utils.converter.OrderConverter;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.security.SecureRandom;
import java.text.DateFormat;
import java.time.DayOfWeek;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author jsy
 * @description:APS排产结果控制器
 * @date 2025/10/10 14:44
 */
@Slf4j
@Service
public class ApsSchedulingResultService extends TestGenericService {
    @Resource
    BasicEquipSchedulingResultMapper basicEquipSchedulingResultMapper;
    /**
     * @description: 查询排产方案
     * @author: jsy
     * @time: 2025/10/10 14:46
     */
    public List<BasicEquipSchedulingResult> querySchedulingResults(String schedulingId) {
        List<BasicEquipSchedulingResult> schedulingResults = JSONUtil.toList((String) RedisUtils.getCacheObject("aps:scheduling:result:897994f8-d83e-4674-9998-7c43b8c4b07e"), BasicEquipSchedulingResult.class);
        return schedulingResults;
    }

    /**
     * @description: 选择排产方案
     * @author: jsy
     * @time: 2025/10/10 14:46
     */
    public void selectSchedulingPlan(String plan, String planIndex) {
        List<BasicEquipSchedulingResult> schedulingResults = JSONUtil.toList((String) RedisUtils.getCacheObject("aps:scheduling:result:" + plan), BasicEquipSchedulingResult.class);
        //根据plan，index 筛选出满足的排产结果
        schedulingResults = schedulingResults.stream().filter(item -> item.getPlan().equals(plan) && item.getPlanIndex().equals(planIndex)).toList();
        //排产结果保存到数据库
        basicEquipSchedulingResultMapper.insert(schedulingResults, 500);
    }

    /**
     * @description: 查询甘特图
     * @author: jsy
     * @time: 2025/10/10 14:46
     */
    public List<Device> queryGanttChart(GanttChartParameter parameter) {
        parameter.setTenantId(getCurrentTenantId());
        List<Map<String, Object>> schedulingResults = basicEquipSchedulingResultMapper.queryGanttChart(parameter);
        //查询其他字段
        List<GanttChartDto> ganttChartDtos = BeanUtil.copyToList(schedulingResults, GanttChartDto.class);

        List<Device> deviceList = convert(ganttChartDtos);
        deviceList = mergeTasksByProcessEndDate(deviceList);
        if (CollUtil.isNotEmpty(deviceList)) {
            return deviceList;
//            if (!"hour".equals(parameter.getTimeUnit()) && !"day".equals(parameter.getTimeUnit()) && !"week".equals(parameter.getTimeUnit()) && !"month".equals(parameter.getTimeUnit())) {
//                throw new IllegalArgumentException("不支持的时间单位，仅支持：hour、day、week、month");
//            }

//            // 根据时间单位获取对应的时间格式化器
//            DateTimeFormatter formatter = getFormatterByTimeUnit(parameter.getTimeUnit());
//
//            // 分组逻辑：使用时间格式化后的字符串作为key
//            return ganttChartDtos.stream()
//                    .filter(result -> result.getProcessEndTime() != null)
//                    .collect(Collectors.groupingBy(
//                            result -> formatTime(result.getProcessEndTime(), formatter, parameter.getTimeUnit()),
//                            TreeMap::new, // 有序Map，默认按key升序
//                            Collectors.collectingAndThen(
//                                    Collectors.toList(),
//                                    list -> list.stream()
//                                            .sorted(Comparator.comparing(GanttChartDto::getProcessEndTime)) // 分组内按开始时间升序
//                                            .collect(Collectors.toList())
//                            )
//                    ));
        }
        return Collections.emptyList();
    }

    /**
     * @description: 根据时间单位获取对应的格式化器
     * @author: jsy
     * @time: 2025/10/10 17:10
     */
    private static DateTimeFormatter getFormatterByTimeUnit(String timeUnit) {
        switch (timeUnit) {
            case "hour":
                return DateTimeFormatter.ofPattern("yyyy-MM-dd HH");
            case "day":
                return DateTimeFormatter.ofPattern("yyyy-MM-dd");
            case "week":
                return DateTimeFormatter.ofPattern("yyyy-MM-dd");
            case "month":
                // 月份格式明确为 "年-月" 格式
                return DateTimeFormatter.ofPattern("yyyy-MM");
            // ISO周格式：yyyy-Www（如2024-W21表示2024年第21周）
//                return DateTimeFormatter.ISO_WEEK_DATE;
            default:
                throw new IllegalArgumentException("不支持的时间单位，仅支持：hour、day、week、month");
        }
    }

    /**
     * @description: 格式化时间
     * @author: jsy
     * @time: 2025/10/10 17:10
     */
    private static String formatTime(LocalDateTime time, DateTimeFormatter formatter, String timeUnit) {
        if ("week".equals(timeUnit)) {
            // ISO周格式中年份可能与自然年不同，如需自然年可自行调整
            // 转换为日期（忽略时间部分）
            LocalDate date = time.toLocalDate();
            // 获取本周周一（若当前是周一则取当天）
            LocalDate monday = date.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
            // 获取本周周日（若当前是周日则取当天）
            LocalDate sunday = date.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY));
            // 格式化为 "yyyy-MM-dd-yyyy-MM-dd" 形式（例如：2024-05-20-2024-05-26）
            return monday.format(formatter) + "-" + sunday.format(formatter);
        }
        return time.format(formatter);
    }


    /**
     * @description: GanttChartDto 转 Device
     * @author: jsy
     * @time: 2025/10/13 17:49
     */
    public static List<Device> convert(List<GanttChartDto> ganttChartDtos) {
        if (ganttChartDtos == null || ganttChartDtos.isEmpty()) {
            return new ArrayList<>();
        }

        // 按设备唯一标识（equipCode）分组
        Map<String, List<GanttChartDto>> ganttByEquip = ganttChartDtos.stream()
                .collect(Collectors.groupingBy(GanttChartDto::getEquipCode));

        // 每组转换为一个Device对象
        return ganttByEquip.entrySet().stream()
                .map(entry -> {
                    String equipCode = entry.getKey();
                    List<GanttChartDto> gantts = entry.getValue();

                    // 创建设备对象并设置基本属性
                    Device device = new Device();
                    // 取分组内第一个对象的设备名称和类型（默认同组设备属性一致）
                    GanttChartDto firstGantt = gantts.get(0);
                    device.setEquipName(firstGantt.getEquipName());
                    device.setEquipType(firstGantt.getEquipType());
                    // 状态、利用率等属性若无直接映射，可设默认值或根据业务补充
                    device.setStatus("进行中"); // 示例默认值
                    device.setStatusType("success"); // 示例默认值
                    device.setUtilization(0); // 示例默认值

                    // 转换任务列表
                    List<Task> tasks = gantts.stream()
                            .map(gantt -> {
                                Task task = new Task();
                                task.setProductName(gantt.getProductName());
                                task.setProcessProductName(gantt.getProcessProductName());
                                // 假设父类BasicEquipSchedulingResult包含时间字段（根据甘特图业务常识）
                                task.setProcessStartTime(gantt.getProcessStartTime());
                                task.setProcessEndTime(gantt.getProcessEndTime());
                                // 其他任务属性按业务需求设置默认值或映射
                                task.setProgress(80);
                                task.setProcessCount(gantt.getProcessCount());
                                task.setStatus("执行中"); // 示例默认值
                                task.setStatusType("primary"); // 示例默认值
                                task.setColor(getRandomColor()); // 可根据业务逻辑设置颜色
                                return task;
                            })
                            .collect(Collectors.toList());

                    device.setTasks(tasks);
                    return device;
                })
                .collect(Collectors.toList());
    }

    /**
     * @description: 合并设备列表中任务的相同processEndTime年月日的数据
     * 并后任务的processStartTime取最早时间，processEndTime取最晚时间
     * @author: jsy
     * @time: 2025/10/13 20:25
     */
    public List<Device> mergeTasksByProcessEndDate(List<Device> deviceList) {
        if (deviceList == null || deviceList.isEmpty()) {
            return Collections.emptyList();
        }

        // 遍历每个设备处理其任务列表
        deviceList.forEach(device -> {
            List<Task> originalTasks = device.getTasks();
            if (originalTasks == null || originalTasks.isEmpty()) {
                return; // 无任务则不处理
            }

            // 按processEndTime的年月日分组（过滤掉null的情况）
            Map<LocalDate, List<Task>> groupedTasks = originalTasks.stream()
                    .filter(task -> task.getProcessStartTime() != null)
                    .collect(Collectors.groupingBy(
                            task -> task.getProcessStartTime().toLocalDate() // 提取年月日作为分组键
                    ));

            // 处理分组任务：合并每组任务
            List<Task> mergedTasks = groupedTasks.values().stream()
                    .map(taskGroup -> {
                        Task mergedTask = new Task();
                        // 取分组中第一个任务的基础字段（非时间字段）
                        Task firstTask = taskGroup.get(0);
                        mergedTask.setProductName(firstTask.getProductName());
                        mergedTask.setProcessProductName(firstTask.getProcessProductName());
                        mergedTask.setProgress(firstTask.getProgress());
                        mergedTask.setProcessCount(firstTask.getProcessCount());
                        mergedTask.setStatus(firstTask.getStatus());
                        mergedTask.setStatusType(firstTask.getStatusType());
                        mergedTask.setColor(firstTask.getColor());

                        // 计算组内最早的processStartTime
                        LocalDateTime minStartTime = taskGroup.stream()
                                .map(Task::getProcessStartTime)
                                .filter(Objects::nonNull)
                                .min(LocalDateTime::compareTo)
                                .orElse(null);
                        mergedTask.setProcessStartTime(minStartTime);

                        // 计算组内最晚的processEndTime
                        LocalDateTime maxEndTime = taskGroup.stream()
                                .map(Task::getProcessEndTime)
                                .max(LocalDateTime::compareTo)
                                .orElse(null);
                        mergedTask.setProcessEndTime(maxEndTime);

                        return mergedTask;
                    })
                    .collect(Collectors.toList());

            // 处理processStartTime为null的任务（直接保留，不参与合并）
            List<Task> nullStartTimeTasks = originalTasks.stream()
                    .filter(task -> task.getProcessStartTime() == null)
                    .collect(Collectors.toList());
            mergedTasks.addAll(nullStartTimeTasks);

            // 将合并后的任务列表设置回设备
            device.setTasks(mergedTasks);
        });

        return deviceList;
    }

    /**
     * @description: 生成随机颜色的工具方法 随机十六进制颜色字符串（格式：#RRGGBB）
     * @author: jsy
     * @time: 2025/10/13 20:24
     */
    public static String getRandomColor() {
        // 十六进制颜色字符集（0-9, A-F）
        String hexChars = "0123456789ABCDEF";
        SecureRandom random = new SecureRandom();
        StringBuilder color = new StringBuilder("#");

        // 生成6位十六进制字符（RRGGBB）
        for (int i = 0; i < 6; i++) {
            // 随机从字符集中选取一个字符
            int index = random.nextInt(hexChars.length());
            color.append(hexChars.charAt(index));
        }
        return color.toString();
    }

}
